#include <QMessageBox>
#include <QFile>
#include <QFileDialog>
#include <QtGui>


#include "OSDaB-Zip/zip.h"
#include "OSDaB-Zip/unzip.h"

#include "defs.h"
#include "mainwindow.h"
#include "pacient.h"
#include "ui_mainwindow.h"
#include "manager.h"
#include "domparser.h"
#include <QSqlQuery>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow),
    msgBoxAskIfNeededToSave(NULL),
    saveFileDialog(NULL)
{
    ui->setupUi(this);

    foreach (QLineEdit *w, ui->passportWidget->findChildren<QLineEdit *>())
    {
        connect( w, SIGNAL(textChanged(QString)),
                 this, SLOT(markAsModified()) );
    }
    connect( ui->passportWidget->findChild<QDateEdit*>("dateEdtDob"),
             SIGNAL(dateChanged(QDate)),
             this, SLOT(markAsModified()));

    connect( ui->passportWidget->findChild<QComboBox*>("cbSex"),
             SIGNAL( currentIndexChanged(int)),
             this, SLOT(markAsModified()));

    connect( ui->passportWidget->findChild<QTextEdit*>("teComment"),
             SIGNAL(textChanged()),
             this, SLOT(markAsModified()));
    connect( ui->passportWidget->findChild<QComboBox*>("cbBloodType"),
             SIGNAL(currentIndexChanged(int)),
             this, SLOT(markAsModified()) );
    connect( ui->passportWidget->findChild<QComboBox*>("cbBloodRh"),
             SIGNAL(currentIndexChanged(int)),
             this, SLOT(markAsModified()) );

    connect( ui->passportWidget->findChild<QCheckBox*>("cbSmoke"),
             SIGNAL(toggled(bool)),
             this, SLOT(markAsModified()) );
    connect( ui->passportWidget->findChild<QCheckBox*>("cbAlko"),
             SIGNAL(toggled(bool)),
             this, SLOT(markAsModified()) );
    connect( ui->passportWidget->findChild<QCheckBox*>("cbNarco"),
             SIGNAL(toggled(bool)),
             this, SLOT(markAsModified()) );
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::setup()
{
    setWindowTitle("QRDNK [*]");
    ui->operationsWidget->setup();
    ui->inspectionstWidget->setup();
    ui->rentgenWidget->setup();
    ui->passportWidget->setup();

//    ui->edtCurFolder->setText( App->getCurrentFolder() );

    //    changePage(P_Start); // ///////////////// DEBUG ///////////////
    changePage(P_Pacient);
    setWindowModified(false);
}

void MainWindow::clearAll()
{
    App->createPacient();
    App->currentZipPath.clear();
    App->currentUnpackedPath.clear();

    ui->passportWidget->clearAll();
    ui->anaysWidget->clearAll();
    ui->operationsWidget->clearAll();
    ui->inspectionstWidget->clearAll();
    ui->rentgenWidget->clearAll();
}

bool compress(const QString& zip, const QString& dir, const QString& pwd)
{
    QFileInfo fi(dir);
    if (!fi.isDir()) {
        qDebug() << "Directory does not exist." << endl << endl;
        return false;
    }

    Zip::ErrorCode ec;
    Zip uz;

    ec = uz.createArchive(zip);
    if (ec != Zip::Ok) {
        qDebug() << "Unable to create archive: " << uz.formatError(ec).toAscii().data() << endl << endl;
        return false;
    }

    uz.setPassword(pwd);

    QDir d(dir);
    QStringList sl( d.entryList() );
    sl.removeOne(QString("."));
    sl.removeOne(QString(".."));

    for (int i=0; i<sl.count(); ++i)
    {
        sl[i].prepend(QString("%1%2")
                      .arg( App->curTempDir().path())
                      .arg(QDir::separator()));
    }
    sl.removeOne( App->currentUnpackedPath );

    ec = uz.addFiles(sl);  //  addDirectory(dir);
    if (ec != Zip::Ok) {
        qDebug() << "Unable to add directory: " << uz.formatError(ec).toAscii().data() << endl << endl;
    }

    uz.setArchiveComment("This archive has been created using QRDNK by N. Gerasymcnuk.");

    if (uz.closeArchive() != Zip::Ok) {
        qDebug() << "Unable to close the archive: " << uz.formatError(ec).toAscii().data() << endl << endl;
    }

    return ec == Zip::Ok;
}

void MainWindow::savePacient(const QString &path)
{
    // SAVE PAC TO CURRENT
    QFileInfo fi(path);
    QString fileName ( fi.fileName().replace(QString(".zip"), QString(".xml")) );
    if ( App->pacient()->saveToXml( App->fileInCurrent( fileName ) ) )
    {
        LOG(objectName(), UkrString("Файл збережено до %1").arg(path));
    }

    // PACK FROM CURRENT
    compress(path, App->curTempDir().path(), QString());

    setWindowModified(false);

    // remove temp file
    QDir dir (App->curTempDir());
    dir.remove(fileName);
}

void MainWindow::on_actAboutQt_triggered()
{
    QApplication::aboutQt();
}

void MainWindow::on_actClearAll_triggered()
{
    if (isWindowModified())
    {
        int res = execAskIfNeededToSave();
        switch (res)
        {
        case QMessageBox::Cancel: return; break;
        case QMessageBox::Discard: break;
        case QMessageBox::Save:
            if (App->currentZipPath.isEmpty())
            {
                selectWhereToSaveAndSave();
            } else {
                savePacient( App->currentZipPath );
            }
            break;
        default: break;
        }
    }
    clearAll();
}

bool decompress(const QString& file, const QString& out, const QString& pwd)
{
    if (!QFile::exists(file)) {
        qDebug() << "File does not exist." << endl << endl;
        return false;
    }
    UnZip::ErrorCode ec;
    UnZip uz;
    if (!pwd.isEmpty())
        uz.setPassword(pwd);

    ec = uz.openArchive(file);
    if (ec != UnZip::Ok) {
        qDebug() << "Failed to open archive: " << uz.formatError(ec).toAscii().data() << endl << endl;
        return false;
    }

    ec = uz.extractAll(out);
    if (ec != UnZip::Ok) {
        qDebug() << "Extraction failed: " << uz.formatError(ec).toAscii().data() << endl << endl;
        uz.closeArchive();
        return false;
    }
    return true;
}

bool MainWindow::openPacient(const QString &path)
{
    if (path.isEmpty()) { return false; }
    App->clearCurrentFolder();
    bool decompressResult = decompress(path, App->curTempDir().path(), "");
    if ( !decompressResult )
    {
        LOG(objectName(), QString("Error decompressing %1").arg(path));
        return false;
    }
    App->createPacient();
    setup();
    App->currentZipPath = path;
    //    App->currentUnpackedPath = App->curTempDir();
    //    qDebug() << "Zip: " << App->currentZipPath << "unpacked: " << App->currentUnpackedPath;

    QDir cur(App->curTempDir());
    // GOT XML
    cur.setNameFilters( QStringList() << QString("*.xml") );
    if ( cur.entryList().isEmpty() )
    {
        LOG(objectName(), QString("No .xml file found in %1 ").arg(path));
        return false;
    }

    qDebug() << App->curTempDir() << cur.entryList() ;
    App->currentUnpackedPath =
            App->curTempDir().absoluteFilePath(cur.entryList().at(0));

    // PARSE XML
    QFile xmlFile(App->currentUnpackedPath);
    if(xmlFile.open(QIODevice::ReadOnly))
    {
        DomParser *parser = new DomParser(&xmlFile, App->pacient());
        if (parser->parseSuccess)
        {
            emit pacientOpened();
            LOG(objectName(), QString("Successful opening of file %1").arg(App->currentUnpackedPath));
        } else {
            LOG(objectName(),
                QString("Error while parsing %1").arg(App->currentUnpackedPath),
                parser->parseResult);
            return false;
        }
    } else {
        LOG(objectName(), QString("Error while opening of file %1").arg(App->currentUnpackedPath));
        return false;
    }
    return true;
}

bool MainWindow::openPacientFromDb(const int id)
{
    Q_ASSERT(App->dbWorker()->workDB()->isOpen());

    // SELECT from PACIENT
    QSqlQuery q(*(App->dbWorker()->workDB()) );
    bool res = q.exec(QString("SELECT * FROM PACIENTS WHERE id=%1").arg(id));
    if (!res)
    {
        return false;
    }
    q.next();
    QVariantList vl;
    for (int i=0; i<PacientFieldCount; ++i)
    {
        vl.append( q.value(i) );
    }
    App->pacient()->setFromVariantList(vl);
    vl.clear();

    // SELECT from INSPECTIONS
    res = q.exec(QString("SELECT * FROM INSPECTIONS WHERE id=%1").arg(id));
    if (res)
    {
        while(q.next())
        {
            QVariantList vl;
            for (int i=1; i<InspectionColCount+1; ++i)
            {
                vl.append( q.value(i) );
            }
            for (int i=IC_UStegno; i<IC_Prognoz; ++i)
            {
                if (vl.at(i)==QString(""))
                vl[i]=QString("");
            }
            vl[IC_Date] = QDate::fromString( vl.at(IC_Date).toString(), SqlDateFormat );
            vl[IC_Age] = vl.at(IC_Age).toDouble();
            vl[IC_Limb] = vl.at(IC_Limb).toInt();
            vl[IC_Prognoz] = vl.at(IC_Prognoz).toInt();
            App->pacient()->addInspection(vl);
        }
    }

    // SELECT from OPERATIONS
    res = q.exec(QString("SELECT * FROM OPERATIONS WHERE id=%1").arg(id));
    if (res)
    {
        while(q.next())
        {
            QVariantList vl;
            for (int i=1; i<InspectionColCount+1; ++i)
            {
                vl.append( q.value(i) );
            }
            vl[OC_Date] = QDate::fromString( vl.at(OC_Date).toString(), SqlDateFormat );
            vl[OC_Age] = vl.at(OC_Age).toDouble();
            vl[OC_Limb] = vl.at(OC_Limb).toInt();
            vl[OC_Bone] = vl.at(OC_Bone).toInt();
            vl[OC_Location] = vl.at(OC_Location).toInt();
            vl[OC_Method] = vl.at(OC_Method).toInt();
            vl[OC_Comment] = vl.at(OC_Comment).toString();
            App->pacient()->addOperation(vl);
        }
    }

    // SELECT from PHOTOES
    res = q.exec(QString("SELECT * FROM OPERATIONS WHERE id=%1").arg(id));
    if (res)
    {
        QString photoName = q.value( 1 ).toString(); // name
        QVariant pic = q.value( 2 );
        QFile photoFile( App->curTempDir().absoluteFilePath(photoName) );
        photoFile.resize(0);
        photoFile.write( pic.toByteArray() );
        photoFile.close();

        ui->passportWidget->loadFoto( App->curTempDir().absoluteFilePath(photoName) );
    }


    // SELECT from RENTGENS
    res = q.exec(QString("SELECT * FROM RENTGENS WHERE id=%1").arg(id));
    if (res)
    {
        while(q.next())
        {
            QVariantList vl;
            for (int i=1; i<RENT_COL_COUNT+2; ++i) // 2 -- бо є image, blob
            {
                vl.append( q.value(i) );
            }
            vl[RentName] = vl.at( RentName ).toString();
            vl[RentSrc] = vl.at( RentSrc ).toString();
            vl[RentPpc] = vl.at( RentPpc).toInt();
            vl[RentComment] = vl.at( RentComment).toString();

            QVariant pic = q.value( RENT_COL_COUNT+1 ); // image, blob
            QFile photoFile( App->curTempDir().absoluteFilePath( vl.at(RentName).toString() ) );
            photoFile.resize(0);
            photoFile.write( pic.toByteArray() );
            photoFile.close();

            App->pacient()->addRentgen( vl );
        }
    }
    return true;
}

void MainWindow::updatePacientsList()
{
    mPacientModel.setQuery(QString("SELECT id, fName || ' ' || mName || ' ' || lName, dob, comment  FROM PACIENTS"), *(App->dbWorker()->workDB()) );

    ui->tblPacients->setModel( &mPacientModel );
    ui->tblPacients->horizontalHeader()->setResizeMode(QHeaderView::ResizeToContents);
    ui->tblPacients->horizontalHeader()->setStretchLastSection(true);
}

void MainWindow::on_actOpenPacient_triggered()
{
    changePage(P_Avaible);
}

void MainWindow::showPacientOnForm()
{
    // ======================================================================
    // відображаю клас Pacient на формі
    ui->passportWidget->setup();

    if (App->pacient()->operationsCount()>0)
        ui->operationsWidget->setEnabledBtnDel();

    if (App->pacient()->inspectionsCount()>0)
        ui->inspectionstWidget->setEnabledBtnDel();

    if (App->pacient()->rentgensCount()<0)
    {
        ui->rentgenWidget->setButtonsEnabled(false);
    } else {
        ui->rentgenWidget->setButtonsEnabled(true);
    }
    setWindowTitle(QString("%1 - %2 [*]")
                   .arg("QRDNK")
                   .arg(App->pacient()->fullName()));
    setWindowModified(false);
}

void MainWindow::decheckAll()
{
    ui->actStart->setChecked(false);
    ui->actPacient->setChecked(false);
    ui->actAnalisys->setChecked(false);
    ui->actHelp->setChecked(false);
    ui->actOpenPacient->setChecked(false);
}

void MainWindow::changePage(const PAGES p)
{
    decheckAll();
    ui->stackedWidget->setCurrentIndex(int(p));

    switch (p) {
    case P_Start:
        ui->actStart->setChecked(true);
        ui->menuBar->actions().at(1)->setVisible(false); // INFO: 1 means Pacient
        break;
    case P_Pacient:
        ui->actPacient->setChecked(true);
        ui->menuBar->actions().at(1)->setVisible(true); // INFO: 1 means Pacient
        break;
    case P_Avaible:
        ui->actOpenPacient->setChecked(true);
        ui->menuBar->actions().at(1)->setVisible(false); // INFO: 1 means Pacient
        break;
    case P_Analisys:
        ui->actAnalisys->setChecked(true);
        break;
    case P_Help:
        ui->actHelp->setChecked(true);
        break;
    default: break;
    }
}

void MainWindow::selectWhereToSaveAndSave()
{
    if (saveFileDialog == NULL)
    {
        saveFileDialog = new QFileDialog(this, UkrString("Оберіть файл для збереження"),
                                         QDir::currentPath(), InOutFileTypes);
    }
    saveFileDialog->selectNameFilter( InOutFileTypes );
    saveFileDialog->setAcceptMode( QFileDialog::AcceptSave );

    if ( saveFileDialog->exec() )
    {
        QString fileName = saveFileDialog->selectedFiles().at(0);
        if (fileName.isNull())
        {
            return;
        }
        fromFormToPacient();
        if (!fileName.endsWith(QString(".zip")))
            fileName.append(".zip");
        savePacient(fileName);
    }
}

void MainWindow::fromFormToPacient()
{
    App->pacient()->setFName(ui->passportWidget->name());
    App->pacient()->setLName(ui->passportWidget->surname());
    App->pacient()->setMName(ui->passportWidget->secName());
    App->pacient()->setDob(ui->passportWidget->dob());
    App->pacient()->setHFather(ui->passportWidget->fathersHeight());
    App->pacient()->setHMother(ui->passportWidget->mothersHeight());
    App->pacient()->setSex(ui->passportWidget->isMan()? "чол" : "жін");
    App->pacient()->setFoto(ui->passportWidget->fotoPath());
    App->pacient()->setComment(ui->passportWidget->comment());
}

PassportWidget *MainWindow::passportWidget()
{
    return ui->passportWidget;
}

InspectionsWidget *MainWindow::inspectionsWidget()
{
    return ui->inspectionstWidget;
}

int MainWindow::execAskIfNeededToSave()
{
    if (msgBoxAskIfNeededToSave==NULL)
    {
        msgBoxAskIfNeededToSave = new QMessageBox();
    }
    msgBoxAskIfNeededToSave->setText(UkrString("<h3>Зберегти зміни даних в поточному пацієнті?</h3>"));
    msgBoxAskIfNeededToSave->setInformativeText(UkrString("Дані поточного пацієнта були змінені. \n"
                                                          "Оберіть Зберегти щоб зберегти дані поточного пацієнта в файл,\n"
                                                          "Ігнорувати -- не зберігати змінипоточного пацієнта в файл,\n"
                                                          "Відміна -- не відкривати файл.\n"));
    msgBoxAskIfNeededToSave->setTextFormat(Qt::RichText);
    msgBoxAskIfNeededToSave->setIcon(QMessageBox::Question);
    msgBoxAskIfNeededToSave->setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
    msgBoxAskIfNeededToSave->setDefaultButton(QMessageBox::Save);

    return msgBoxAskIfNeededToSave->exec();
}

void MainWindow::createConnectionsToPacient()
{
    ui->passportWidget->createConnectToPacient();
}

void MainWindow::closeEvent(QCloseEvent *e)
{
    if (isWindowModified())
    {
        int res = execAskIfNeededToSave();
        switch (res)
        {
        case QMessageBox::Cancel: e->ignore(); return; break;
        case QMessageBox::Discard: e->accept(); break;
        case QMessageBox::Save:
            if (App->currentZipPath.isEmpty())
            {
                selectWhereToSaveAndSave();
            } else {
                savePacient( App->currentZipPath );
            }
            break;
        default: break;
        }
    }
    QMainWindow::closeEvent(e);
}

void MainWindow::on_actSavePaciet_triggered()
{

}

void MainWindow::on_actAbot_triggered()
{
    QString infoText (
                UkrString("<h2><center>Програмний засіб QRDNK</center></h2>"
                          "<br /><center><img src=:logo align=middle width=200></center><br />"
                          "Розроблений для використання в Інституті травматології та ортопедії Національної академії медичних наук України."
                          "<ul>"
                          "<li>Розробник <b>Герасимчук Н. О.</b> (<a href=\"mailto:Nazar.Gerasymchuk@gmail.com\">написати листа</a>)</li>"
                          "<li>Науковий керівник <b>Бичков О. С.</b> </li>"
                          "<li>Науковий консультант <b>Нікітченко М. С.</b> </li>"
                          "<li>Консультант з предметної області <b>Сивак М. Ф.</b> </li>"
                          "</ul>") );

    QMessageBox::about(this, UkrString("Про програму"), infoText);
}

void MainWindow::on_actStart_triggered()
{
    changePage(P_Start);
}

void MainWindow::on_actPacient_triggered()
{
    changePage(P_Pacient);
}

void MainWindow::on_actAnalisys_triggered()
{
    changePage(P_Analisys);
}

void MainWindow::on_actHelp_triggered()
{
    changePage(P_Help);
}

void MainWindow::on_actImport_triggered()
{
    if (isWindowModified())
    {
        int res = execAskIfNeededToSave();
        switch (res)
        {
        case QMessageBox::Cancel: return; break;
        case QMessageBox::Discard: break;
        case QMessageBox::Save:
            if (App->currentZipPath.isEmpty())
            {
                selectWhereToSaveAndSave();
            } else {
                savePacient( App->currentZipPath );
            }
            break;
        default: break;
        }
    }

    QString fileName = QFileDialog::getOpenFileName(this, UkrString("Відкрити файл"),
                                                    "",
                                                    InOutFileTypes);
    App->mainWindow()->clearAll();

    if (!fileName.isEmpty())
    {

        if ( !openPacient(fileName) )
        {
            QMessageBox msg(QMessageBox::Warning,
                            UkrString("Проблема при відкриття файлу"),
                            UkrString("Виникла помилка при відкриванні файлу:\n %1.\n"
                                      "Для детальної інформації дивіться журнал виконання\n"
                                      "програми.")
                            .arg(fileName),
                            QMessageBox::Ok);
            msg.exec();
        } else {
            App->currentZipPath = fileName;
            changePage(P_Pacient);
        }
    }

    // TODO: ЗБЕРЕГТИ СОБІ В ПАПКУ
}

void MainWindow::on_toolButton_clicked() ///////////////////////////////////////////////////////////////////////////
{
    // Folder dialog

    QFileDialog dialog(this, UkrString("Оберіть каталог з файлами пацієнтів"));
    dialog.setFileMode(QFileDialog::Directory);
    dialog.setOption(QFileDialog::ShowDirsOnly, true);

    if (dialog.exec())
    {
        QString path (dialog.selectedFiles().at(0));
        qDebug() << "curFolder: " << path;
        App->setCurrentFolder( path );
//        ui->edtCurFolder->setText( path );
    }
}

void MainWindow::on_actAddPacient_triggered()
{
    if (isWindowModified())
    {
        int res = execAskIfNeededToSave();
        switch (res)
        {
        case QMessageBox::Cancel: return; break;
        case QMessageBox::Discard: break;
        case QMessageBox::Save:
            if (App->currentZipPath.isEmpty())
            {
                selectWhereToSaveAndSave();
            } else {
                savePacient( App->currentZipPath );
            }
            break;
        default: break;
        }
    } else {
        App->createPacient();
        clearAll();
        changePage(P_Pacient);
    }
}


void MainWindow::markAsModified()
{
    //    static int count=0;
    //    qDebug () << "MARK: " << count++;
    setWindowModified(true);
}

void MainWindow::on_actExit_triggered()
{
    if (isWindowModified())
    {
        int res = execAskIfNeededToSave();
        switch (res)
        {
        case QMessageBox::Cancel: return; break;
        case QMessageBox::Discard: break;
        case QMessageBox::Save:
            if (App->currentZipPath.isEmpty())
            {
                selectWhereToSaveAndSave();
            } else {
                savePacient( App->currentZipPath );
            }
            break;
        default: break;
        }
    }
    QCoreApplication::quit();
}

void MainWindow::on_actExport_triggered()
{
    selectWhereToSaveAndSave();
}

void MainWindow::on_actExportToHTML_triggered()
{
    QString fileName = QFileDialog::getSaveFileName(this, "Виберіть файл, в який зберегти", "", ".html");
    if (fileName.isEmpty()) return;
    if (fileName.right(4)!=".html")
        fileName.append(".html");
    App->pacient()->saveToHtml(fileName);
}

void MainWindow::on_actPrint_triggered()
{
    App->printPacient();
}

void MainWindow::on_actionCreate_DB_triggered()
{
    App->dbWorker()->createDb("testDb.sqlite");
}

void MainWindow::on_btnChangeCurDbFile_clicked()
{
    QString fileName = QFileDialog::getOpenFileName(this, "Виберіть файл бази даних", "",
                                                    UkrString("Файл бази даних (*.sqlite)") );
    if (fileName.isEmpty()) return;

    bool opened = App->dbWorker()->openDb( fileName );
    if (opened)
    {
        updatePacientsList();
        ui->edtCurDBFile->setText(fileName);
        // TODO: зберегти назву поточної бази десь в заначку
    }
}

void MainWindow::on_tblPacients_doubleClicked(const QModelIndex &index)
{
    if (isWindowModified())
    {
        int res = execAskIfNeededToSave();
        switch (res)
        {
        case QMessageBox::Cancel: return; break;
        case QMessageBox::Discard: break;
        case QMessageBox::Save:
            if (App->currentZipPath.isEmpty())
            {
                selectWhereToSaveAndSave();
            } else {
                savePacient( App->currentZipPath );
            }
            break;
        default: break;
        }
    }

    App->mainWindow()->clearAll();

    const int row = index.row();
    const int curId = index.model()->data(index.model()->index(row, 0)).toInt();

    qDebug() << "openPacientFromDb: " << openPacientFromDb( curId );
}
